package cn.jx.cjm.security.support;

import cn.jx.cjm.common.enums.BizExceptionEnum;
import cn.jx.cjm.common.enums.StatusEnum;
import cn.jx.cjm.common.exception.BizException;
import cn.jx.cjm.common.util.BeanUtils;
import cn.jx.cjm.security.common.bean.TokenUser;
import cn.jx.cjm.security.common.config.UserTokenProperties;
import cn.jx.cjm.security.common.util.RouterUtils;
import cn.jx.cjm.security.common.util.SecurityUtils;
import cn.jx.cjm.security.dto.request.SysUserSelfRequest;
import cn.jx.cjm.security.dto.response.SysUserResponse;
import cn.jx.cjm.security.dto.response.SysUserSelfResponse;
import cn.jx.cjm.security.entity.SysPermission;
import cn.jx.cjm.security.entity.SysUser;
import cn.jx.cjm.security.service.ISysUserService;
import cn.jx.cjm.thirdparty.cache.CacheStorageService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.List;


/**
 * 管理员信息 业务处理层
 *
 * @author James Chen
 * @since 2019-12-16
 */

@Service
public class SysUserSelfSupportService implements UserDetailsService {

    private static final String ERROR_TIMES_PREFIX = "ERROR-TIMES-USER-%s";

    @Autowired
    private CacheStorageService cacheStorageService;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private UserTokenProperties userTokenProperties;
    @Autowired
    private SysPermissionSupportService sysPermissionSupportService;


    /**
     * 加载用户信息
     *
     * @param username 用户登录账号
     * @return .
     * @throws UsernameNotFoundException 用户不存在异常
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (StringUtils.isEmpty(username)) {
            throw new BizException(BizExceptionEnum.CHECK_FIELD);
        }
        TokenUser tokenUser = new TokenUser();
        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);
        SysUser sysUser = sysUserService.getOne(queryWrapper);
        if (null == sysUser) {
            throw new UsernameNotFoundException("用户名不存在！");
        }
        //基本信息
        BeanUtils.copyPropertiesIgnoreNull(sysUser, tokenUser);

        LocalDateTime now = LocalDateTime.now();
        tokenUser.setEnabled(StatusEnum.NORMAL.getCode().equals(sysUser.getStatus()));
        tokenUser.setAccountNonExpired(null != sysUser.getAccountExpiredTime() && sysUser.getAccountExpiredTime().isAfter(now));
        tokenUser.setAccountNonLocked(null == sysUser.getAccountLockTime() || sysUser.getAccountLockTime().isBefore(now));
        tokenUser.setCredentialsNonExpired(null != sysUser.getLastModifyPasswordTime() && sysUser.getLastModifyPasswordTime().isAfter(now.plusDays(-30)));

        return tokenUser;
    }

    /**
     * 获取用户信息
     *
     * @return .
     */
    public SysUserSelfResponse getCurrentSysUser() {
        TokenUser tokenUser = SecurityUtils.getSessionUser();
        SysUser sysUser = sysUserService.getById(tokenUser.getId());
        //基本信息
        SysUserSelfResponse sysUserResponse = new SysUserSelfResponse();
        BeanUtils.copyPropertiesIgnoreNull(sysUser, sysUserResponse);
        BeanUtils.copyPropertiesIgnoreNull(tokenUser, sysUserResponse);
        // 权限信息
        List<SysPermission> sysPermissionList = sysPermissionSupportService.getSysPermissionList();
        if (!CollectionUtils.isEmpty(sysPermissionList)) {
            //拼装路由 及 权限
            RouterUtils routerUtils = new RouterUtils(sysPermissionList);
            sysUserResponse.setRouters(routerUtils.createRouter());
            sysUserResponse.setPermissions(routerUtils.createPermissions());
        }
        // 部门信息

        return sysUserResponse;
    }


    /**
     * 更新自身信息
     * @param request
     */
    public void update(SysUserSelfRequest request) {

        TokenUser tokenUser = SecurityUtils.getSessionUser();
        SysUser sysUser = new SysUser();
        sysUser.setId(tokenUser.getId());
        sysUser.setRealName(request.getRealName());
        sysUser.setAvatar(request.getAvatar());
        sysUser.setPhone(request.getPhone());
        sysUser.setEmail(request.getEmail());
        sysUserService.updateById(sysUser);
    }


    /**
     * 更新密码
     *
     * @param request 管理员信息
     */
    public void updatePassword(SysUserSelfRequest request) {

        QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", request.getUsername());

        SysUser sysUser = sysUserService.getOne(queryWrapper);
        if (null == sysUser) {
            throw new BizException(BizExceptionEnum.SAVE_ERROR.getCode(), "原密码错误");
        }
        Integer id = sysUser.getId();
        //判断原始密码正确与否
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        if (!bCryptPasswordEncoder.matches(request.getPassword(), sysUser.getPassword())) {
            throw new BizException(BizExceptionEnum.SAVE_ERROR.getCode(), "原密码错误");
        }

        //更新密码
        sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setPassword(bCryptPasswordEncoder.encode(request.getNewPassword()));
        sysUser.setLastModifyPasswordTime(LocalDateTime.now());
        sysUserService.updateById(sysUser);
    }


    /**
     * 更新用户上次登录时间
     * @param id
     */
    public void updateLastLoginTime(Integer id) {
        SysUser sysUser = new SysUser();
        sysUser.setId(id);
        sysUser.setLastLoginTime(LocalDateTime.now());
        sysUserService.updateById(sysUser);
    }

    /**
     * 记录用户密码错误次数
     * @param username
     */
    public void addPasswordErrorTimes(String username) {

        if (cacheStorageService.overTimeNum(String.format(ERROR_TIMES_PREFIX, username))) {
            LocalDateTime lockTime = LocalDateTime.now().plusMinutes(userTokenProperties.getLockMinutes());
            UpdateWrapper<SysUser> sysUserUpdateWrapper = new UpdateWrapper<>();
            sysUserUpdateWrapper.eq("username", username);
            sysUserUpdateWrapper.set("account_lock_time", lockTime);
            sysUserService.update(new SysUser(), sysUserUpdateWrapper);
        }
    }
}

